package com.xuyuan.json.jackson.spring;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;

public class MappingJackson2HttpMessageConverterGbkSupport extends MappingJackson2HttpMessageConverter {
    private static final Charset GBK = Charset.forName("GBK");

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        if (!super.canWrite(mediaType)) {
            return false;
        }
        boolean canWrite = super.canWrite(clazz, mediaType);
        if (canWrite) {
            return true;
        }
        // 判断是否GBK编码格式
        if (mediaType != null && mediaType.getCharset() != null) {
            Charset charset = mediaType.getCharset();
            return charset.equals(GBK);
        }
        return false;
    }

    @Override
    protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        MediaType contentType = outputMessage.getHeaders().getContentType();
        logger.debug(contentType);

        Charset charset;
        if (contentType == null || contentType.getCharset() == null) {
            charset = getAcceptCharset();
            if (charset == null) {
                charset = getContentTypeCharset();
            }
            if (charset == null) {
                charset = getDefaultCharset();
            }
        } else {
            charset = contentType.getCharset();
        }

        logger.debug(charset);

        // 是否GBK编码
        if (!GBK.equals(charset)) {
            super.writeInternal(object, type, outputMessage);
            return;
        }

        contentType = contentType == null ? MediaType.APPLICATION_JSON : contentType;
        MediaType mediaType = new MediaType(contentType, GBK);

        HttpHeaders headers = outputMessage.getHeaders();
        headers.setContentType(mediaType);
        headers.add(HttpHeaders.ACCEPT, mediaType.toString());


        Writer reader = new OutputStreamWriter(outputMessage.getBody(), GBK);
        super.defaultObjectMapper.writeValue(reader, object);
    }

    private Charset getContentTypeCharset() {
        Object contentType = FlowRuntimeContext.getTechProperty(HttpHeaders.CONTENT_TYPE);
        if (contentType == null || contentType.toString().isEmpty()) {
            return null;
        }
        MediaType mediaType = MediaType.valueOf(contentType.toString());
        return mediaType.getCharset();
    }

    private Charset getAcceptCharset() {
        Object accept = FlowRuntimeContext.getTechProperty(HttpHeaders.ACCEPT);
        if (accept == null || accept.toString().isEmpty()) {
            return null;
        }
        String[] headerValueArray = accept.toString().split(",");
        List<String> headerValues = Arrays.asList(headerValueArray);
        List<MediaType> mediaTypes = MediaType.parseMediaTypes(headerValues);
        MediaType.sortBySpecificityAndQuality(mediaTypes);

        for (MediaType mediaType : mediaTypes) {
            for (MediaType supportedMediaType : getSupportedMediaTypes()) {
                if (supportedMediaType.isCompatibleWith(mediaType) && mediaType.getCharset() != null) {
                    return mediaType.getCharset();
                }
            }
        }
        return null;
    }
}
